package org.luxor.commons.schedule.config;

import org.luxor.commons.schedule.config.properties.QuartzExtendProperties;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;

import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 定时器持久化集群配置
 *
 * @author Mr.Yan  @date 2020/3/2
 */
@Order(999)
@Configuration
@ConditionalOnClass({Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class})
@EnableConfigurationProperties({QuartzProperties.class, QuartzExtendProperties.class})
@ConditionalOnProperty(prefix = "spring.quartz", name = "job-store-type", havingValue = "jdbc")
public class ScheduleConfiguration {

    private final QuartzExtendProperties extendProperties;

    private final QuartzProperties properties;

    private final List<SchedulerFactoryBeanCustomizer> customizers;

    private final JobDetail[] jobDetails;

    private final Map<String, Calendar> calendars;

    private final Trigger[] triggers;

    private final ApplicationContext applicationContext;

    public ScheduleConfiguration(QuartzExtendProperties extendProperties,
                                 QuartzProperties properties,
                                 ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
                                 ObjectProvider<JobDetail[]> jobDetails,
                                 ObjectProvider<Map<String, Calendar>> calendars,
                                 ObjectProvider<Trigger[]> triggers,
                                 ApplicationContext applicationContext) {
        this.extendProperties = extendProperties;
        this.properties = properties;
        this.customizers = customizers.getIfAvailable();
        this.jobDetails = jobDetails.getIfAvailable();
        this.calendars = calendars.getIfAvailable();
        this.triggers = triggers.getIfAvailable();
        this.applicationContext = applicationContext;
    }

    @Bean
    public SchedulerFactoryBean quartzScheduler() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(
                this.applicationContext.getAutowireCapableBeanFactory()));
        schedulerFactoryBean.setQuartzProperties(asProperties(this.properties.getProperties()));

        if (this.jobDetails != null && this.jobDetails.length > 0) {
            schedulerFactoryBean.setJobDetails(this.jobDetails);
        }
        if (this.calendars != null && !this.calendars.isEmpty()) {
            schedulerFactoryBean.setCalendars(this.calendars);
        }
        if (this.triggers != null && this.triggers.length > 0) {
            schedulerFactoryBean.setTriggers(this.triggers);
        }

        // 集群实例名
        schedulerFactoryBean.setSchedulerName(extendProperties.getInstanceName());
        // 延时启动
        schedulerFactoryBean.setStartupDelay(30);
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可选，QuartzScheduler 启动时更新己存在的Job，这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 设置自动启动，默认为true
        schedulerFactoryBean.setAutoStartup(extendProperties.isEnabled());

        // 定制配置
        customize(schedulerFactoryBean);
        return schedulerFactoryBean;
    }

    private void customize(SchedulerFactoryBean schedulerFactoryBean) {
        if (this.customizers != null) {
            for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
                customizer.customize(schedulerFactoryBean);
            }
        }
    }

    private Properties asProperties(Map<String, String> source) {
        //Quartz默认参数
        Properties properties = new Properties();
        properties.put("org.quartz.scheduler.instanceName", extendProperties.getInstanceName());
        properties.put("org.quartz.scheduler.instanceId", "AUTO");
        //线程池配置
        properties.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        properties.put("org.quartz.threadPool.threadCount", "20");
        properties.put("org.quartz.threadPool.threadPriority", "5");
        //JobStore配置
        properties.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        //集群配置
        properties.put("org.quartz.jobStore.isClustered", "true");
        properties.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        properties.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");

        properties.put("org.quartz.jobStore.misfireThreshold", "12000");
        properties.put("org.quartz.jobStore.tablePrefix", extendProperties.getTablePrefix());
        properties.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");

        if (!source.isEmpty()) {
            properties.putAll(source);
        }
        return properties;
    }

}
